<?php
/*  Copyright 2011 princehaku
 *
 *  Author     : princehaku
 *  Site       : http://3haku.net
 *
 *  注意 : 请不要将此脚本用于商业用途..
 *  此脚本完全是模拟了一个浏览器让用户自主操作，不存在破解或重打包客户端等行为。
 *  此脚本以学习为目的，不涉及任何商业利益。任何企业和个人与此接口有关的商业行为，请与移动公司联系。
 *  任何人使用此脚本而造成的不良后果，均由使用者承担，与此脚本的作者没有任何关系。

/*
 define("DEBUG",0);

 if(DEBUG==1){
 error_reporting(9);
 }
 else
 {
 error_reporting(0);
 }
 */
/**用curl做的联网类
*/

/*
ini_set('display_errors',1);
ini_set('display_startup_errors',1);
error_reporting(E_ALL);
*/
@date_default_timezone_set('Asia/Shanghai');
 if( !function_exists('log_message')){

	function log_message($level='debug',$message='',$path=TMPPATH)
	{
		static $file;
		if(!$file) {
			$file = rtrim($path,'/') .'/log_'.date('ymd_Hi') .'.txt';
		}
		$fp = fopen($file,'a');
		$data =date('r') . ": {$level}:  {$message}\r\n";
		flock($fp,LOCK_EX);
		fwrite($fp,$data);
		flock($fp, LOCK_UN); // 只能手动解锁，在php5.3.2+, fclose并不解锁
		fclose($fp);
	}

 }

class httpconnector{
	/**Curl类
	 *
	 */
	private $curl;
	/**cookie字符串
	 */
	private $cookie = '';
	/**源(用于最后结果调试)
	 */
	private $sourceWmlStack=array();
	/**得到源wml栈
	 */
	public function getSource(){
		return $this->sourceWmlStack;
	}
	/**get方式下载网页内容
	 *@param $url
	 *@return web conntent
	 */
	public function get($url){

		$this->curl = curl_init();

		curl_setopt($this->curl, CURLOPT_URL, $url);

		// 设置header
		curl_setopt($this->curl, CURLOPT_HEADER, 1);

		//个人以为应该在此判断$this->cookie是否为空再执行
		//The contents of the "Cookie: " header to be used in the HTTP request. Note that multiple cookies are separated with a semicolon followed by a space (e.g., "fruit=apple; colour=red")
		curl_setopt ($this->curl, CURLOPT_COOKIE , $this->cookie);

		// 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($this->curl,CURLOPT_CONNECTTIMEOUT,5);
		// 运行cURL，请求网页
		$data = curl_exec($this->curl);
		// 关闭URL请求
		curl_close($this->curl);
		//找到cookie 放入cookiestring
/*
		preg_match("/Set-Cookie:(.*?);/",$data,$r);

		if(@$r[1]!=""){
			if($this->cookie==""){
				$this->cookie .= str_replace("\r\n","",$r[1]);
			}
		}
*/
		$this->set_cookie($data);

		log_message('debug','抓取url: '.$url);
		log_message('debug','此时cookie: '.$this->cookie);
		log_message('debug','抓取返回data: '."\n".$data);

		//放入调试栈
		array_push($this->sourceWmlStack,$data);
		//log_message('debug',$data);
		return $data;

	}

	/**POST方式下载网页内容
	 *@param $url
	 *@param $params post的信息串
	 *@return web conntent
	 */
	public function post($url,$params){

		$this->curl = curl_init();

		curl_setopt($this->curl, CURLOPT_URL, $url);

		// 设置header
		curl_setopt($this->curl, CURLOPT_HEADER, 1);

		curl_setopt ($this->curl, CURLOPT_COOKIE , $this->cookie);//发送带Cookie的请求

		curl_setopt($this->curl, CURLOPT_POST, 1);

		curl_setopt($this->curl, CURLOPT_POSTFIELDS, $params);

		// 设置cURL 参数，要求结果保存到字符串中还是输出到屏幕上。
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($this->curl,CURLOPT_CONNECTTIMEOUT,5);
		// 运行cURL，请求网页
		$data = curl_exec($this->curl);

		// 关闭URL请求
		curl_close($this->curl);
		//找到cookie 放入cookiestring
		$this->set_cookie($data);

		log_message('debug','POST提交url : '.$url);
		log_message('debug','提交参数: '.$params);
		log_message('debug','此时cookie'.$this->cookie);
		log_message('debug','抓取返回data: '."\n".$data);


		//放入调试栈
		array_push($this->sourceWmlStack,$data);

		return $data;

	}

	//添加cookie
	private function set_cookie ($data)
	{
		$m = preg_match_all('#Set-Cookie:(.*)\s#',$data,$matches);
		if($m) {
			$arr = implode('; ',array_map('trim',$matches[1]));
			//var_dump($matches);
			$arr2 = explode('; ',$arr);
			$arr2 = array_unique($arr2,SORT_STRING );
			foreach ($arr2 as $k=>$v) {
				//如果在原cookie中存在字段，不加入
				$v = trim($v);
				if(strpos($this->cookie,$v) !==FALSE) {
					unset($arr2[$k]);
				}
			}
			//附加到原cookie中
			if (count($arr2)>0){
				$this->cookie .=  '; ' . implode('; ',$arr2);
			}

		}
		$this->cookie = trim($this->cookie,'; ');
		$this->cookie = trim($this->cookie,' ');
		return $this;

	}

}
/**飞信类
 */
class Fection{

	private $r;
	private $name='Far';

	private $httpconnector;
	/**发送方手机号码
	 */
	private $phonenum;
	/**发送方飞信密码
	 */
	private $pwd;
	/**是否成功登陆
	 */
	private $islogin=0;

	//登陆失败前最大尝试次数
	private $try_limit=3;

	//手机号码类型
	const IS_PID = 0;

	//飞信号类型
	const IS_FID = 1;
	/**操作记录
	 */
	private $status=array();

	private $timeout = 10;

	function track($action,$statu,$phone=""){
		if($phone==""){
			$phone=$this->phonenum;
		}
		$r=array("action"=>$action,"statu"=>$statu,"phone"=>$phone);
		array_push($this->status,$r);
	}
	function __construct($config=array()){

		$this->phonenum=$config['my_phone_num'];
		$this->pwd=$config['my_password'];
		if( isset($config['my_name']) && !empty($config['my_name'])){
			if(mb_strlen($config['my_name'],'utf-8')>5)
				throw new Fection_exception ($this,'名称不能大于5个字符或汉字');
			$this->name = $config['my_name'];

		}
		if(isset($config['tmppath'])) {
			$tmppath = $config['tmppath'];
		}elseif( !isset($config['tmppath']) && defined('TMPPATH')){
			$tmppath = TMPPATH;
		}else {
			throw new Fection_exception($this,'临时目录不能为空');
		}

		$this->tmp_flag = $tmppath.'fection.txt';
		$time = 0;
		log_message('debug','Begin to init the fection class');
		//全局范围内保证只有一个飞信登录操作，限制并行操作
		while( file_exists($this->tmp_flag)){
			sleep(1);
			$time++;

			//如果上次执行退出时没有删除此文件，为避免永久无法执行，强制删除，
			//与上次间隔30s
			clearstatcache();
			if(time()-filemtime($this->tmp_flag)>30){
				unlink($this->tmp_flag);
				log_message('debug','强制删除上次执行错误的文件');
				break;
			}

			if($time >$this->timeout) {
				log_message('error','fection 等待超时');
				throw new Fection_exception($this,'等待超时');
			}
		}


		touch ($this->tmp_flag);
		$this->httpconnector=new httpconnector();
		//得到ssid
		$this->httpconnector->get('http://f.10086.cn/im/login/login.action');

		//开始登录
		$try_limit = $this->try_limit;//最大尝试连接次数
		$try = 0;
		while(TRUE) {
			if(FALSE !== $this->login()) {
				break;
			}else{
				$try++;
				if($try>=$try_limit){
					log_message('error','达最大尝试次数'.$this->try_limit .'，登录失败 ！');
					unlink($this->tmp_flag);
					throw new Fection_exception($this,'达最大尝试次数，飞信网络连接失败 ！');

				}
			}

		}


	}
	/**登陆飞信
	 */
	function login(){

		$login_url = 'http://f.10086.cn/im/login/inputpasssubmit1.action';
		$login_data = "loginstatus=4&m=$this->phonenum&pass=$this->pwd";
		$data=$this->httpconnector->post($login_url,$login_data);
		if( strpos($data,'登录失败') !==FALSE){
			$this->islogin=0;
			return FALSE;
		}

		$m = preg_match("/ontimer=\"(.*?)\"/",$data,$r);
		if($m ===FALSE) {
			return FALSE;
		}else {
			if (!isset($r[1])) return FALSE;

			$r=parse_url($r[1]);
			$r=explode("=",$r['query']);

			$this->t=$r['1'];
			if($this->t==""){
				$this->track("Login","faild");
				$this->islogin=0;
				$this->desc="Login Faild!";
				return FALSE;

			}
			else{
				$this->track("Login","success");
				$this->islogin=1;
				$this->desc="Login Success!";
				log_message('debug','登录成功'.'t值 : '.$this->t);
			}
			return $this;
		}
	}

	function is_login()
	{
		return (bool)($this->islogin);
	}
	/**退出飞信
	 */
	function logout(){

		if($this->islogin){
			$return = $this->httpconnector->get("http://f.10086.cn/im/index/logoutsubmit.action?t=$this->t");
			if (strpos($return,'成功退出')) {
				if (file_exists($this->tmp_flag))	{
					unlink($this->tmp_flag);
				}
				return TRUE;
			}
		}
		return FALSE;


	}
	/** 给自己发送短信
	 *
	 * @param mixed $msg
	 */
	function sendToSelf($msg){
		if(!$this->islogin){
			return FALSE;
		}

		$data =$this->httpconnector->post("http://f.10086.cn/im/user/sendMsgToMyselfs.action?t=$this->t","msg=$msg");

		if((int)strpos($data,"成功")>10){
			$this->track("SendToSelf","success");
			$this->desc="Send To Self Success!";
			return TRUE;
		}


		$this->track("SendToSelf","faild");
		$this->desc="Send To Self Success!";
		return FALSE;
	}
	/**
	 * 给某飞信id发送消息
	 *
	 * @param mixed $fid
	 * @param mixed $msg
	 * @param mixed $towho
	 */
	function sendTofid($fid,$msg,$towho=''){
		if(!$this->islogin){
			return FALSE;
		}
		if($towho==""){
			$towho="0";
		}
		//touchTitle字段值得探索
		$data=$this->httpconnector->post("http://f.10086.cn/im/chat/sendMsg.action?t=$this->t&touserid=$fid","msg=$msg&touchTextLength=&touchTitle=");

		if((int)strpos($data,"成功")>10){
			$this->track("SendMessage","success",$towho);
			//$this->savefidLocal($towho,$fid);
			$this->desc="Send Message Success!";
			return TRUE;
		}

		$this->track("SendMessage","faild",$towho);
		$this->desc="Send Message Faild!";
	}

	function sendsmsTofid($fid,$msg,$towho=''){
		if(!$this->islogin){
			return FALSE;
		}
		if($towho==""){
			$towho="0";
		}

		$data=$this->httpconnector->post("http://f.10086.cn/im/chat/sendShortMsg.action?t=$this->t&touserid=$fid","msg=$msg&touchTextLength=&touchTitle=");

		if((int)strpos($data,"成功")>10){
			$this->track("SendSMS","success",$towho);
			//$this->savefidLocal($towho,$fid);
			$this->desc="Send SMS Success!";
			return TRUE;
		}else{
			return FALSE;
		}

		$this->track("SendSMS","faild",$towho);
		$this->desc="Send SMS Faild!";
	}
	/**发送飞信短信
	 *@param $fid 接收方的手机号码或者昵称
	 *@param $msg 消息正文
	 */
	function send($towho,$msg,$ifsms){
		if(!$this->islogin){
			return;
		}
		$fid=$this->phonenum;
		//如果不是给自己发送的转换uid
		if($towho!=$this->phonenum){
			$fid=$this->getfidLocal($towho);
			if($fid==""){
				$fid=$this->getfidNet($towho);
			}
		}
		if($fid!=$this->phonenum&&$fid==""){
			$this->track("tofid","faild",$towho);
			$this->desc="Search FetionID Failed!";
			return;
		}
		$data="";

		if($towho==$this->phonenum)     {
			$this->sendToSelf($msg);
		}else{
			if ($ifsms=="1")
			$this->sendsmsTofid($fid,$msg,$towho);
			else
			$this->sendTofid($fid,$msg,$towho);
		}
	}
	/**添加用户为好友
	 */
	function addfriend($num,$type='',$name='',$localName=''){
		$num =trim($num);
		$type =(empty($type)) ? $this->auto_type($num) : $type;
		$name = (empty($name)) ? $this->name :$name; //我的名字
		//$localName =( empty($localName))?'F'.date('mdHis') :$localName; //用户显示名字
		
		$localName =empty($localName)?$num:$localName; 
		//localName是本地显示的名称吗？Yes，typ参数有什么意义? 1为飞信类型，0为手机类型
		$pdata = "nickname=$name&buddylist=2&localName=$localName&number=$num&type=".$type;
		log_message('debug',"num :{$num}  type:{$type}");
		//$pdata = "nickname=fc&buddylist=2&localName=&number={$num}&type=1";
		$data=$this->httpconnector->post("http://f.10086.cn/im/user/insertfriendsubmit.action?t=$this->t",$pdata);

		//var_dump($data);
		if((int)strpos($data,"对不起,")>0){
			$this->track("AddFriend","faild",$num);
			$this->desc="Add Friend Faild!";
			return FALSE;
		}

		$this->track("AddFriend","success",$num);
		$this->desc="Add Friend Success!";
		return TRUE;
	}

	/**
	* 按时间生成本地用户名
	*/
	static public function name_generator()
	{
		//这个可以用62或者64进制
		return 'F'.date('mdHis');

	}


	/**用于将手机号或者飞信号或者昵称转换为飞信id
	 * param $val
	 */
	function getfidNet($val){
		if(!$this->islogin){
			return FALSE;
		}
		$search_url = "http://f.10086.cn/im/index/searchOtherInfoList.action?t=$this->t";
		$search_args ="searchText=$val";
		$data=$this->httpconnector->post($search_url,$search_args);
		log_message('debug','在好友列表搜索'.$data);
		//通过电话号使用搜索好友的，对方须已经在好友列表中
		if(strpos($data,'姓名过长')) throw new Fection_exception($this,'搜索姓名过长');
		if(strpos($data,'没有符合条件')) {
			log_message('debug','没有符合条件的好友');
			return FALSE;
		}
		$name = '匿名用户';
		$get_name = preg_match('#/im/chat.*?>(.*?)</a>#',$data ,$match);
		if($get_name) {
			if($match[1]) {
				$name = $match[1];
			}
		}


		$pos1=(int) strpos($data,"touserid=");
		$pos2=(int) strpos($data,"&",$pos1);
		$uid="";
		if($pos1>0&&$pos2>0){
			$uid=substr($data,$pos1+9,$pos2-$pos1-9);
		}
		return array($uid,$name);
	}

	/**
	*通过查找飞信号或手机号确定是否用户是否存在
	* 类型:0为飞信，1为手机号,不填时系统自动判断
	*
	* 通过飞信号判断时不可靠，wap上的touserid 和电脑客户端fection号的并不完全一致！
	*@param $num  手机号或飞信号
	*@param $typ 0或1, 留空时系统自动判断
	*/
	function is_fection_user($num,$type='')
	{	$num =trim($num);
		if(empty($type)){
			$type=$this->auto_type($num);
		}
		log_message('debug',"num :{$num}  type:{$type}");
		$search_url = 'http://f.10086.cn/im/user/insertfriend2.action';
		$pdata = "type=$type&number=$num";
		$return = $this->httpconnector->post($search_url,$pdata);

		//var_dump($return);
		if(strpos($return,'您要添加的好友是') !==FALSE || strpos($return,'用户已经是您的好友') !==FALSE){
			log_message('debug',"{$num} 是飞信用户");
			return TRUE;
		}
		else  {
			return FALSE;
		}

	}

	private function auto_type($num)
	{
		if( !$this->islogin)
			throw new Fection_exception ($this,'请先登录');

		$len = strlen($num);
		switch($len){
			case 9:
				$type=self::IS_FID;
				break;
			case 11:
				$type=self::IS_PID;
				break;
			default:
				throw new Fection_exception ($this,'请输入正确的飞信号或手机号');
		}

		return $type;
	}


	function __destruct() {
		$this->logout();
	}
}

class Fection_exception extends Exception
{
	function __construct($fection,$message='',$code=0,$previous = NULL ){
		//log_message('debug','飞信异常处理');
		if( $fection instanceof  Fection && $fection->is_login()) $fection->logout();
		log_message('debug',$message);
		//第三个参数在php5.2不可用！否则会报错
		//5.3.0 The previous parameter was added ,
		parent::__construct($message,$code);
	}

}

define('TMPPATH','D:/51szhj/fection/tmp');
$config = array (
	'name' =>'免费天气',
	'my_phone_num'=>'13556152752',
	'my_password'=>'7340985cxm',
);

/*测试方法全部通过！*/
$fection = new Fection($config);
$phone1 = '13480999585';
$phone2 =  '18998637580'; //not fection user
$phone3 = '13540318954';
$phone4 = '13760894425';//汤
$phone5 = '13751829667'; //腾飞
$phone_arr[] = '13714274903'; //注册用户1
$phone_arr[] = '13866380057'; //注册用户2
$phone_arr[] = '13500504649';//注册用户3
$phone9 = '13437846158';//昌哥
$phone10 = '13510993849'; //老吴
$phone11 = '15817471856'; //注册用户
//$phone = $phone8;
//检查手机号是否为飞信用户

$person_info = $fection->getfidNet($phone1);
echo '取得用户个人信息';
var_dump($person_info); //fid = 289071182
echo "</br >";
$mess = '哦，几点能回来?';
$mess_rs = $fection->sendTofid($person_info[0],$mess);
var_dump($mess_rs);
$fection->logout();

die();
foreach($phone_arr as $phone) {

/*
$rt = $fection->is_fection_user($phone);

echo '检查手机号是否为飞信用户';
var_dump($rt);
echo "</br >";
*/
/*
$friend_rt = $fection->addfriend($phone);

echo '加好友结果';
var_dump($friend_rt);
echo "</br >";
*/
//取用户个人信息,必须在用户被加为好友之后，只要上面的结果成功（即addfriend返回真)就可以了，甚至不需要对方同意。
$person_info = $fection->getfidNet($phone);

echo '取得用户个人信息';
var_dump($person_info);
echo "</br >";

//测试给好友发信息
/*
if(is_array($person_info)) {
	$mess = '发送消息方法测试：飞信天气感谢你的注册和反馈，请回复Y确认收到消息。^_^';
	$mess .='尽管失败和挫折等待着人们，一次次地夺走青春的容颜，但却给人生的前景增添了一份尊严，这是任何顺利的成功都不能做到的。--梭罗';
	//$mess = '呵呵,昌哥最近在哪快活？';
	$mess_rs = $fection->sendTofid($person_info[0],$mess);
	echo '给好友发消息:';
	var_dump($mess_rs);
	echo "</br >";

}
*/
//给自己发信息
$mes ='尽管失败和挫折等待着人们，一次次地夺走青春的容颜，但却给人生的前景增添了一份尊严，这是任何顺利的成功都不能做到的。--梭罗';
$self_rt = $fection->sendToSelf($mes.date('H:i:s',time()));

echo '给自己发消息:';
var_dump($self_rt);
echo "</br >";

}
$fection->logout();




/*
 $phone=@$_GET["phone"];
 $pwd=@$_GET["pwd"];
 $to=@$_GET["to"];
 $msg=@$_GET['msg'];
 $ifsms=@$_GET["ifsms"];
 if(!($phone&&$pwd&&($msg&&$to||$add))){
 die("参数不全 或者直接打开了API文件 <a href='http://www.bber.info'> http://www.bber.info/</a>");
 }
 $fection=new fection($phone,$pwd);
 $fection->login();
 //添加好友的操作
 if($add!=""){
 $res=explode(",",$add);
 foreach($res as $i=>$adds){
 $fection->addfriend($adds,@$_GET['name']);
 }
 }
 else{
 //发送短信的操作
 $res=explode(",",$to);
 foreach($res as $i=>$tos){
 $fection->send($tos,$msg,$ifsms);
 }
 }

 $fection->logout();
 //打印结果
 $fection->toXml();
 */
 
 
 
 
 
 
 
/*
$phone = '13556152752';
$password = '7340985cxm';
$falcon = new fection($phone,$password);
$falcon->login();
$msg = 'test :'.date('H:i:s',time());
$fid = '623224759';//我的飞信id
$peng['fid'] = '245152878';//鹏
$peng['phone'] = '13602494329';//peng手机
$fig_friend = '472199841';//秋菊
$friend_phone = '13570930963';//天荣
$chenxiaoxia['fid'] = '289071182';
$chenxiaoxia['phone'] = '13480999585';
$msg = 'Test '.time();
$falcon->addfriend('13430361938');

//$falcon->sendToSelf('哥也伤不起 :'.date('H:i:s',time()));
//$falcon->sendsmsTofid($chenxiaoxia['fid'],$msg); //OK

//$fid = $falcon->getfidNet('15814537192');
//查找自己的fid时失败

//$fid = $falcon->getfidNet($chenxiaoxia['phone']);
var_dump($falcon);
//var_dump($falcon);
$falcon->logout();
*/
?>